package com.sxw.aop;

import com.sxw.annotation.PreventDuplicateSubmit;
import com.sxw.exception.BizException;
import com.sxw.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class DuplicateSubmitAspect {
    
    private final RedisUtils redisUtils;
    
    /**
     * 拦截带有@PreventDuplicateSubmit注解的方法
     */
    @Around("@annotation(preventDuplicateSubmit)")
    public Object around(ProceedingJoinPoint joinPoint, PreventDuplicateSubmit preventDuplicateSubmit) throws Throwable {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        
        // 构建Redis锁的key
        String key = buildRedisKey(joinPoint, preventDuplicateSubmit.keyPrefix());
        
        // 尝试获取分布式锁
        String lockValue = String.valueOf(System.currentTimeMillis());
        boolean lockAcquired = redisUtils.tryLock(key, lockValue, preventDuplicateSubmit.expireTime());
        
        if (!lockAcquired) {
            // 获取锁失败，说明有重复提交
            log.warn("检测到重复提交，请求参数: {}", key);
            throw new BizException("请勿重复提交");
        }
        
        try {
            // 执行原方法
            return joinPoint.proceed();
        } finally {
            // 释放锁
            redisUtils.releaseLock(key, lockValue);
        }
    }
    
    /**
     * 根据方法参数构建Redis key
     */
    private String buildRedisKey(ProceedingJoinPoint joinPoint, String keyPrefix) {
        StringBuilder sb = new StringBuilder(keyPrefix);
        
        // 添加类名和方法名
        sb.append(joinPoint.getTarget().getClass().getSimpleName()).append(":");
        sb.append(joinPoint.getSignature().getName()).append(":");
        
        // 添加参数值的MD5摘要
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            StringBuilder paramBuilder = new StringBuilder();
            for (Object arg : args) {
                if (arg != null) {
                    paramBuilder.append(arg.toString());
                }
            }
            String params = paramBuilder.toString();
            if (StringUtils.hasText(params)) {
                String md5 = DigestUtils.md5DigestAsHex(params.getBytes(StandardCharsets.UTF_8));
                sb.append(md5);
            }
        }
        
        return sb.toString();
    }
}